﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight;

namespace DataInterpolator.ViewModel
{
   public class IsoPlotStatsVMO : ObservableObject
   {
      private InternalStats _IsoPach = new InternalStats();
      private InternalStats _IsoTime = new InternalStats();
      private InternalStats _IsoVel = new InternalStats();

      public IsoPlotStatsVMO()
      {
         TimePach = new TwinStatsVMO(_IsoTime, _IsoPach);
         TimeVel = new TwinStatsVMO(_IsoTime, _IsoVel);
         PachVel = new TwinStatsVMO(_IsoPach, _IsoVel);
      }

      public TwinStatsVMO TimePach
      {
         get; private set;
      }

      public TwinStatsVMO TimeVel
      {
         get; private set;
      }

      public TwinStatsVMO PachVel
      {
         get; private set;
      }

      public void Update(IList<WellVMO> wells)
      {
         List<double> isoPachData = new List<double>();
         List<double> isoTimeData = new List<double>();
         List<double> isoVelData = new List<double>();

         foreach (var well in wells)
         {
            if (!well.Visible)
               continue;

            isoPachData.Add(well.IsoData.IsoPach);
            isoTimeData.Add(well.IsoData.IsoTime);
            isoVelData.Add(well.IsoData.IntervalVelocity);
         }

         _IsoPach.Update(isoPachData);
         _IsoTime.Update(isoTimeData);
         _IsoVel.Update(isoVelData);

         TimePach.RaiseAllPropertiesChanged();
         TimeVel.RaiseAllPropertiesChanged();
         PachVel.RaiseAllPropertiesChanged();
      }

      public class InternalStats
      {
         public double Mean
         {
            get; set;
         }
         public double StdError
         {
            get; set;
         }
         public double Median
         {
            get; set;
         }
         public double Mode
         {
            get; set;
         }
         public double StdDev
         {
            get; set;
         }
         public double Variance
         {
            get; set;
         }
         public double Kurtosis
         {
            get; set;
         }
         public double Skewness
         {
            get; set;
         }
         public double Range
         {
            get; set;
         }
         public double Minimum
         {
            get; set;
         }
         public double Maximum
         {
            get; set;
         }
         public double Sum
         {
            get; set;
         }
         public int Count
         {
            get; set;
         }
         public double Norm
         {
            get; set;
         }

         public void Update(List<double> data)
         {
            //Reset statistics
            Mean = 0.0;
            StdError = 0.0;
            Median = 0.0;
            Mode = 0.0;
            StdDev = 0.0;
            Variance = 0.0;
            Kurtosis = 0.0;
            Skewness = 0.0;
            Range = 0.0;
            Minimum = double.MaxValue;
            Maximum = double.MinValue;
            Sum = 0.0;
            Count = data.Count;
            Norm = 0.0;

            if (Count == 0)
               return;

            double dCount = Count;
            var sorted = data.OrderByDescending(g => g);

            //Finally, calculate the statistics on the auto correlated data
            for (int i = 0; i < data.Count; i++)
            {
               Sum += data[i];
               Norm += data[i] * data[i];
            }

            Maximum = sorted.First();
            Minimum = sorted.Last();

            Range = Maximum - Minimum;
            Mean = Sum / dCount;
            Norm = Math.Sqrt(Norm);

            //Last one is the standard deviation
            for (int i = 0; i < data.Count; i++)
            {
               double diff = data[i] - Mean;

               diff *= diff;
               Variance += diff;
               diff *= diff;
               Skewness += diff;
               diff *= diff;
               Kurtosis += diff;
            }

            Variance /= dCount - 1.0;
            StdDev = Math.Sqrt(Variance);
            StdError = StdDev / Math.Sqrt(dCount);

            Skewness = ((dCount) / (StdDev * StdDev * StdDev * (dCount - 1) * (dCount - 2))) * Skewness;
            Kurtosis = (((dCount * (dCount + 1.0)) / (Math.Pow(StdDev, 4.0) * (dCount - 1.0) * (dCount - 2.0) * (dCount - 3.0))) * Kurtosis) - ((3.0 * (dCount - 1.0) * (dCount - 1.0)) / ((dCount - 2.0) * (dCount - 3.0)));

            int mid = Count / 2;

            if (Count % 2 > 0)
            {
               //We are odd, should be easy
               Median = sorted.ElementAt(mid);
            }
            else
            {
               Median = (sorted.ElementAt(mid - 1) + sorted.ElementAt(mid)) / 2.0;
            }

            Mode = data.GroupBy(v => v)
                         .OrderByDescending(g => g.Count())
                         .First()
                         .Key;
         }
      }

      public class TwinStatsVMO : ObservableObject
      {
         private InternalStats _Left = null;
         private InternalStats _Right = null;

         public TwinStatsVMO(InternalStats left, InternalStats right)
         {
            _Left = left;
            _Right = right;
         }

         [DisplayName("Mean")]
         [Description("The mean of the data set in the [X, Y] format")]
         public string Mean
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Mean, _Right.Mean);
            }
         }

         [DisplayName("Standard Error")]
         [Description("The StdError of the data set in the [X, Y] format")]
         public string StdError
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.StdError, _Right.StdError);
            }
         }

         [DisplayName("Median")]
         [Description("The Median of the data set in the [X, Y] format")]
         public string Median
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Median, _Right.Median);
            }
         }

         [DisplayName("Mode")]
         [Description("The Mode of the data set in the [X, Y] format")]
         public string Mode
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Mode, _Right.Mode);
            }
         }

         [DisplayName("Standard Deviation")]
         [Description("The StdDev of the data set in the [X, Y] format")]
         public string StdDev
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.StdDev, _Right.StdDev);
            }
         }

         [DisplayName("Variance")]
         [Description("The Variance of the data set in the [X, Y] format")]
         public string Variance
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Variance, _Right.Variance);
            }
         }

         [DisplayName("Kurtosis")]
         [Description("The Kurtosis of the data set in the [X, Y] format")]
         public string Kurtosis
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Kurtosis, _Right.Kurtosis);
            }
         }

         [DisplayName("Skewness")]
         [Description("The Skewness of the data set in the [X, Y] format")]
         public string Skewness
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Skewness, _Right.Skewness);
            }
         }

         [DisplayName("Range")]
         [Description("The Range of the data set in the [X, Y] format")]
         public string Range
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Range, _Right.Range);
            }
         }

         [DisplayName("Minimum")]
         [Description("The Minimum of the data set in the [X, Y] format")]
         public string Minimum
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Minimum, _Right.Minimum);
            }
         }

         [DisplayName("Maximum")]
         [Description("The Maximum of the data set in the [X, Y] format")]
         public string Maximum
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Maximum, _Right.Maximum);
            }
         }

         [DisplayName("Sum")]
         [Description("The Sum of the data set in the [X, Y] format")]
         public string Sum
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Sum, _Right.Sum);
            }
         }

         [DisplayName("Count")]
         [Description("The Count of the data set in the [X, Y] format")]
         public string Count
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Count, _Right.Count);
            }
         }

         [DisplayName("Norm")]
         [Description("The Norm of the data set in the [X, Y] format")]
         public string Norm
         {
            get
            {
               return string.Format("[{0:G8}, {1:G8}]", _Left.Norm, _Right.Norm);
            }
         }

         public void RaiseAllPropertiesChanged()
         {
            RaisePropertyChanged(null);
         }
      }
   }
}
